package wekaLogic;

import java.util.LinkedList;
import java.util.List;

import weka.attributeSelection.ASEvaluation;
import weka.attributeSelection.ASSearch;
import weka.attributeSelection.CfsSubsetEval;
import weka.attributeSelection.GreedyStepwise;
import weka.attributeSelection.PrincipalComponents;
import weka.attributeSelection.Ranker;
import weka.classifiers.AbstractClassifier;
import weka.classifiers.bayes.BayesNet;
import weka.classifiers.meta.AttributeSelectedClassifier;
import weka.classifiers.meta.MultiClassClassifier;
import weka.core.Instance;
import weka.core.Instances;
import weka.core.converters.ConverterUtils.DataSource;
import weka.filters.Filter;
import weka.filters.unsupervised.attribute.NumericToNominal;

public class ClassifierHandler {

	public static int NENHUM = 0;
	public static int PCA = 1;
	public static int CFS = 2;
	public static int BAYES_NET = 3;
	public static int MULTILAYER_PERCEPTRON = 4;
	public static String NOTE_A = "A";
	public static String NOTE_B = "B";
	public static String NOTE_C = "C";

	public static void main(String[] args) {
		try {
			ClassifierHandler handler = new ClassifierHandler();
			List<String> result = handler.classifyNotes("Files/test.csv");
			System.out.println(result);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private AttributeSelectedClassifier trainClassifier() throws Exception {
		Instances data = readDatas("Files/train.csv");
		AttributeSelectedClassifier classifier = getClassifier(data, CFS,
				BAYES_NET);
		return classifier;
	}

	public List<String> classifyNotes(String path) throws Exception {
		AttributeSelectedClassifier classifier = trainClassifier();
		LinkedList<Double> notes = new LinkedList<Double>();
		Instances unlabeled = DataSource.read(path);
		unlabeled.setClassIndex(unlabeled.numAttributes() - 1);
		NumericToNominal filter = new NumericToNominal();
		filter.setInputFormat(unlabeled);
		Instances datasFormated = Filter.useFilter(unlabeled, filter);

		for (int i = 0; i < datasFormated.numInstances(); i++) {
			notes.add(classifier.classifyInstance(datasFormated.get(i)));
		}

		LinkedList<String> outNotes = new LinkedList<String>();

		for (Double note : notes) {
			if (note == 0.0) {
				outNotes.add(NOTE_A);
			} else if (note == 1.0) {
				outNotes.add(NOTE_B);
			} else {
				outNotes.add(NOTE_C);
			}
		}

		return outNotes;
	}

	public Instances readDatas(String path) throws Exception {
		DataSource source = new DataSource(path);
		Instances data = source.getDataSet();
		if (data.classIndex() == -1) {
			data.setClassIndex(data.numAttributes() - 1);
		}

		return data;

	}

	private ASEvaluation getSelector(int selector) {
		if (selector == PCA) {
			return new PrincipalComponents();
		} else if (selector == CFS) {
			return new CfsSubsetEval();
		} else {
			return null;
		}
	}

	private AbstractClassifier getClassifier(int classifier) {
		if (classifier == BAYES_NET) {
			return new BayesNet();
		} else if (classifier == MULTILAYER_PERCEPTRON) {
			return new MultiClassClassifier();
		} else {
			return null;
		}
	}

	private ASSearch getSearch(int selector) {
		if (selector == PCA) {
			Ranker ranker = new Ranker();
			ranker.setGenerateRanking(true);
			return ranker;
		} else if (selector == CFS) {
			GreedyStepwise search = new GreedyStepwise();
			search.setSearchBackwards(true);
			return search;
		} else {
			return null;
		}
	}

	public double getNote(Instance instance,
			AttributeSelectedClassifier classifier) throws Exception {
		return classifier.classifyInstance(instance);

	}

	public AttributeSelectedClassifier getClassifier(Instances data,
			int selector, int classifierType) throws Exception {
		NumericToNominal filter = new NumericToNominal();
		filter.setInputFormat(data);
		Instances dataTrans = Filter.useFilter(data, filter);

		AttributeSelectedClassifier classifier = new AttributeSelectedClassifier();

		classifier.setClassifier(getClassifier(classifierType));
		classifier.setEvaluator(getSelector(selector));
		classifier.setSearch(getSearch(selector));

		classifier.buildClassifier(dataTrans);

		return classifier;
	}
}
